package comnet.chatserver;

import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collection;

/**
 * @author Mikko Puustinen (mikko.puustinen@gmail.com)
 * @version 0.1, 2007-nov-18
 */
public class ChatHandler implements Runnable {
    private Socket socket;
    private BufferedReader in = null;
    private PrintWriter out = null;
    private String nickname;
    private static int usersConnected = 0;
    private static final Collection<ChatHandler> handlers = new ArrayList<ChatHandler>();

    public ChatHandler(Socket socket) throws IOException {
        this.socket = socket;
        in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);

        synchronized(handlers) {
            handlers.add(this);
            usersConnected++;
        }

        nickname = assignNickname();
        out.println("Welcome to this server. Enjoy your stay.");
        out.println("Your nickname is " + nickname + ". To change this send:\r\n\t/nick <nickname>\r\n");
        broadCastMessage(nickname + " connected", this);
    }

    /**
     * When an object implementing interface <code>Runnable</code> is used to
     * create a thread, starting the thread causes the object's <code>run</code>
     * method to be called in that separately executing thread.
     * <p/>
     * The general contract of the method <code>run</code> is that it may take
     * any action whatsoever.
     *
     * @see Thread#run()
     */
    public void run() {
        String line;

        try {
            while ((line = in.readLine()) != null) {
                if (line.startsWith("/quit")) {
                    break;
                } else if (line.startsWith("/who")) {
                    send(printAllConnected());
                } else if (line.startsWith("/nick")) {
                    String[] n = line.split(" ");
                    String temp = nickname;
                    if (n.length > 1) {
                        if (setNickname(n[1]) != null) {
                            nickname = n[1];
                            broadCastMessage("** " + temp + " changed name to " + nickname, null);
                        }

                    }
                } else {
                    broadCastMessage(nickname + "> " + line, this);
                }
            }
        } catch (IOException e) {
            //
        } finally {
            System.out.println("Client (" + nickname + ") disconnected.");
            broadCastMessage("** " + nickname + " disconnected", null);
            synchronized(handlers) {
                handlers.remove(this);
            }
            try {
                in.close();
                out.close();
                socket.close();
            } catch (IOException e) {
                //
            }
        }
    }

    private void broadCastMessage(String msg, ChatHandler user) {
        synchronized(handlers) {
            for (ChatHandler c : handlers) {
                if (c != user)
                    c.send(msg);
            }
        }
    }

    private String printAllConnected() {
        String str = "\n** Connected clients\r\n";
        synchronized(handlers) {
            for (ChatHandler c : handlers) {
                if (c == this) {
                    str += "\t(You) " + getNickname() + "\r\n";
                } else {
                    str += "\t" + c.getNickname() + "\r\n";
                }
            }
        }

        return str;
    }

    private void send(String msg) {
        out.println(msg);
        out.flush();
    }

    private String assignNickname() {
        int i;
        synchronized(handlers) {
            i = usersConnected;
        }
        return "User" + i;
    }

    public String getNickname() {
        return nickname;
    }

    private String setNickname(String nickname) {
        for (ChatHandler c : handlers) {
            if (c != this) {
                if (c.getNickname().equalsIgnoreCase(nickname))
                    return null;
            }
        }
        return nickname;
    }
}
